package hichecker.university;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlValue;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;


/**
 * Repräsentation eines Semesterdatums. Datum wird im Format 2009-04-01 gespeichert und kann im
 * Format WS 09/10 ausgegeben werden. Es stehen Funktionen zur Manipulation des Semesterdatums zur
 * Verfügung.
 *
 * @author Marcel
 */
@XmlRootElement(name = "semester")
public class SemesterDate implements Comparable<SemesterDate> {
	@XmlJavaTypeAdapter( SemesterXMLAdapter.class)
	private @XmlValue String date;

	public SemesterDate() {
		date = null;
	}

	/**
	 * Erzeugt ein neues SemesterDate
	 *
	 * @param date
	 *            Stringrepräsentation des Datum im Format "2009-04-01" bzw. "2009-10-01"
	 */
	public SemesterDate(String date) {
		// Format prüfen
		if (!isValidDate(date))
			throw new NumberFormatException("Illegal SemesterDate Format");

		// Wertzuweisung
		this.date = date;
	}

	public SemesterDate(SemesterDate semester) {
		date = semester.getDate();
	}

	/**
	 * Parst ein SemesterDate aus einem String im Format "SS 09" bzw. "WS 09/10"
	 * @param semesterDate Datum im Format "SS 09" bzw. "WS 09/10"
	 * @return {@link SemesterDate}
	 */
	public static SemesterDate parseSemesterDate(String semesterDate) {
		if (!isValidSemesterDate(semesterDate))
			throw new NumberFormatException("Illegal SemesterDate Format");

		String monthString = semesterDate.substring(0, 2).toLowerCase().equals("ss") ? "04" : "10";
		int year = Integer.parseInt(semesterDate.substring(3, 5));
		String yearString = year < 10 ? "0" + year : "" + year;
		yearString = year < 50 ? "20" + yearString : "19" + yearString;
		return new SemesterDate(yearString + "-" + monthString + "-01");
	}


	/**
	 * Prüft, ob ein Semesterdate im Format "2009-04-01" bzw. "2009-10-01" vorliegt
	 *
	 * @param date
	 *            Stringrepräsentation des Datum im Format "2009-04-01" bzw. "2009-10-01"
	 * @return valid
	 */
	public static boolean isValidDate(String date) {
		if (date == null)
			return false;

		// Prüfung auf strukturellen Aufbau
		if (date.length() != 10 || date.charAt(4) != '-' || date.charAt(7) != '-')
			return false;

		// Prüfüng des Wertebreiches
		try {
			int year = Integer.parseInt(date.substring(0, 4));
			int month = Integer.parseInt(date.substring(5, 7));
			int day = Integer.parseInt(date.substring(8, 10));
			return year > 1900 && year < 2100 && (month == 4 || month == 10) && day == 1;
		} catch (NumberFormatException e) {
			return false;
		}
	}

	/**
	 * Prüft, ob ein Semesterdate im Format "SS 09" bzw. "WS 09/10" vorliegt
	 *
	 * @param semesterDate
	 *            Stringrepräsentation des Datum im Format "SS 09" bzw. "WS 09/10"
	 * @return valid
	 */
	public static boolean isValidSemesterDate(String semesterDate) {
		if (semesterDate == null)
			return false;

		// Sommersemester
		if (semesterDate.length() == 5 && semesterDate.substring(0, 3).toLowerCase().equals("ss ")) {
			try {
				Integer.parseInt(semesterDate.substring(3));
				return true;
			} catch (NumberFormatException e) {
				return false;
			}
		}

		// Wintersemester
		if (semesterDate.length() == 8 && semesterDate.substring(0, 3).toLowerCase().equals("ws ") && semesterDate.charAt(5) == '/') {
			try {
				int year1 = Integer.parseInt(semesterDate.substring(3, 5));
				int year2 = Integer.parseInt(semesterDate.substring(6, 8));
				return (year1 + 1) % 100 == year2;
			} catch (NumberFormatException e) {
				return false;
			}
		}

		// falsches Format
		return false;
	}

	@Override
	public int compareTo(SemesterDate that) {
		return this.date.compareTo(that.date);
	}

	/**
	 * Liefert eine String Repräsentation
	 *
	 * @return Semster im Format "SS 09" bzw. "WS 09/10"
	 */
	@Override
	public String toString() {
		// Jahresinformationen zusammenbauen
		int year = Integer.parseInt(date.substring(2, 4));
		String firstYear = year < 10 ? "0" + year : "" + year;
		String secondYear = (year + 1) % 100 < 10 ? "0" + (year + 1) % 100 : "" + (year + 1) % 100;

		// Sommersemester
		if (date.substring(5, 7).equals("04")) {
			return "SS " + firstYear;
		}

		// Wintersemester
		return "WS " + firstYear + "/" + (secondYear);
	}

	/**
	 * Vergleich zwei SemesterDates miteinander
	 */
	@Override
	public boolean equals(Object o) {
		if (!(o instanceof SemesterDate)) {
			return false;
		}
		SemesterDate that = (SemesterDate) o;
		return this.date.equals(that.date);
	}

	/**
	 * Liefert das Semesterdatum, sodass es es Dateiname verwendet werden kann
	 * @param semester
	 * @return
	 */
	public String fitToFileName() {
		return toString().replace("/", "-");
	}

	/**
	 * Liefert das folgende Semester
	 *
	 * @return SemesterDate
	 */
	public SemesterDate getNext() {
		int year = Integer.parseInt(date.substring(0, 4));
		int month = Integer.parseInt(date.substring(5, 7));
		year = month == 10 ? ++year : year;
		month = month == 10 ? 4 : 10;

		return new SemesterDate(year + (month == 4 ? "-04" : "-10") + "-01");
	}

	/**
	 * Liefert das vorrangegange Semester
	 *
	 * @return SemesterDate
	 */
	public SemesterDate getPrev() {
		int year = Integer.parseInt(date.substring(0, 4));
		int month = Integer.parseInt(date.substring(5, 7));
		year = month == 4 ? --year : year;
		month = month == 10 ? 4 : 10;
		return new SemesterDate(year + (month == 4 ? "-04" : "-10") + "-01");
	}

	/**
	 * Liefert das Semesterdatum im Format "2009-04-01" bzw. "2009-10-01"
	 */
	@XmlTransient
	public String getDate() {
		return date;
	}

	/**
	 * Setzt das Semesterdatum
	 * @param date Stringrepräsentation des Datum im Format "2009-04-01" bzw. "2009-10-01"
	 */
	public void setDate(String date) {
		// Format prüfen
		if (!isValidDate(date))
			throw new NumberFormatException("Illegal SemesterDate Format");

		// Wertzuweisung
		this.date = date;
	}

	private static class SemesterXMLAdapter extends XmlAdapter<String, String> {

		@Override
		public String marshal(String v) throws Exception {
			SemesterDate temp = new SemesterDate(v);
			return temp.toString();
		}

		@Override
		public String unmarshal(String v) throws Exception {
			return (SemesterDate.parseSemesterDate(v)).getDate();
		}

	}

}
